﻿using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;

namespace IAM.Interface.Common.Encrypt
{
    /// <summary>
    /// 加密解密
    /// </summary>
    public class EncryptDecrypt
    {
        private string mKey = string.Empty;
        private EncryptEnum mAlgorithm;
        private SymmetricAlgorithm mCryptoService;
        /// <summary>
        /// 加密使用的Key
        /// </summary>
        public string Key
        {
            get
            {
                return mKey;
            }
            set
            {
                mKey = value;
            }
        }
        public EncryptEnum EncryptType
        {
            get
            {
                return mAlgorithm;
            }
        }
        #region 构造函数
        /// <summary>
        /// 随机产生一种加密方式和密钥
        /// </summary>
        public EncryptDecrypt()
            : this((new Random()).Next(0, 3))
        {
            #region 注销
            //mCryptoService = new RijndaelManaged();
            //mCryptoService.Mode = CipherMode.CBC;
            //mAlgorithm = EncryptEnum.Rijndael;
            //this.Key = System.Guid.NewGuid().ToString();
            #endregion
        }
        /// <summary>
        /// 加密构造函数
        /// </summary>
        /// <param name="encryptEnum">利用encryptEnum来选择一种加密算法</param>
        public EncryptDecrypt(EncryptEnum encryptEnum)
        {
            switch (encryptEnum)
            {
                case EncryptEnum.Rijndael:
                    mCryptoService = new RijndaelManaged();
                    mAlgorithm = EncryptEnum.Rijndael;
                    break;
                case EncryptEnum.RC2:
                    mCryptoService = new RC2CryptoServiceProvider();
                    mAlgorithm = EncryptEnum.RC2;
                    break;
                case EncryptEnum.DES:
                    mCryptoService = new DESCryptoServiceProvider();
                    mAlgorithm = EncryptEnum.DES;
                    break;
                case EncryptEnum.TripleDES:
                    mCryptoService = new TripleDESCryptoServiceProvider();
                    mAlgorithm = EncryptEnum.TripleDES;
                    break;
            }
            this.Key = System.Guid.NewGuid().ToString();
            mCryptoService.Mode = CipherMode.CBC;
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="iee">加密方式的数字表示</param>
        public EncryptDecrypt(int iee)
        {
            EncryptEnum encryptEnum = (EncryptEnum)iee;
            switch (encryptEnum)
            {
                case EncryptEnum.Rijndael:
                    mCryptoService = new RijndaelManaged();
                    mAlgorithm = EncryptEnum.Rijndael;
                    break;
                case EncryptEnum.RC2:
                    mCryptoService = new RC2CryptoServiceProvider();
                    mAlgorithm = EncryptEnum.RC2;
                    break;
                case EncryptEnum.DES:
                    mCryptoService = new DESCryptoServiceProvider();
                    mAlgorithm = EncryptEnum.DES;
                    break;
                case EncryptEnum.TripleDES:
                    mCryptoService = new TripleDESCryptoServiceProvider();
                    mAlgorithm = EncryptEnum.TripleDES;
                    break;
            }
            mCryptoService.Mode = CipherMode.CBC;
            this.Key = System.Guid.NewGuid().ToString();
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="encryptenumname">利用算法名来选择加密算法</param>
        public EncryptDecrypt(string encryptenumname)
        {
            try
            {
                switch (encryptenumname.ToLower())
                {
                    case "rijndael":
                        encryptenumname = "Rijndael";
                        mAlgorithm = EncryptEnum.Rijndael;
                        break;
                    case "rc2":
                        encryptenumname = "RC2";
                        mAlgorithm = EncryptEnum.RC2;
                        break;
                    case "des":
                        encryptenumname = "DES";
                        mAlgorithm = EncryptEnum.DES;
                        break;
                    case "tripledes":
                        encryptenumname = "TripleDES";
                        mAlgorithm = EncryptEnum.TripleDES;
                        break;
                }

                mCryptoService = (SymmetricAlgorithm)CryptoConfig.CreateFromName(encryptenumname);
                mCryptoService.Mode = CipherMode.CBC;
                this.Key = System.Guid.NewGuid().ToString();
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        #endregion
        /// <summary>
        /// 对称算法的机密密钥的长度如下：
        /// 对于DES算法，密钥长度为8字节
        /// 对于TripleDES算法，密钥长度为16或者24字节
        /// 对于Rijndael算法，密钥长度为16，24或者32字节
        /// 可以人工输入，也可以随机生成，方法是：des.GenerateKey();
        /// 对于不符合要求的key，就需要调整其内容
        /// </summary>
        /// <returns></returns>
        public byte[] GetLegalKey()
        {
            //对称算法的机密密钥的长度如下：
            //对于DES算法，密钥长度为8字节
            //对于TripleDES算法，密钥长度为16或者24字节
            //对于Rijndael算法，密钥长度为16，24或者32字节
            //可以人工输入，也可以随机生成，方法是：des.GenerateKey();
            //对于不符合要求的key，就需要调整其内容
            if (mCryptoService.LegalKeySizes.Length > 0)
            {
                // 用位来表示key的大小
                int keySize = mKey.Length * 8;
                int minSize = mCryptoService.LegalKeySizes[0].MinSize;
                int maxSize = mCryptoService.LegalKeySizes[0].MaxSize;
                int skipSize = mCryptoService.LegalKeySizes[0].SkipSize;

                if (keySize > maxSize)
                {
                    // 去除多余的部分
                    mKey = mKey.Substring(0, maxSize / 8);
                }
                else if (keySize < maxSize)
                {
                    // 设定有效大小
                    int validSize = (keySize <= minSize) ? minSize : (keySize - keySize % skipSize) + skipSize;
                    if (keySize < validSize)
                    {
                        // 使用*来填补不足的部分
                        mKey = mKey.PadRight(validSize / 8, '*');
                    }
                }
            }
            PasswordDeriveBytes key = new PasswordDeriveBytes(mKey, ASCIIEncoding.ASCII.GetBytes(string.Empty));
            return key.GetBytes(mKey.Length);
        }
        /// <summary>
        /// 对称算法的初始化向量
        /// 对于DES, TripleDES, RC2向量的长度为8字节
        /// 对于Rijndael算法，向量长度为16字节
        /// 可以人工输入，也可以随机生成，方法是：des.GenerateIV();
        /// 在这儿为了简单起见，我们使用固定的值，
        /// 也可以按照调整key的办法来调整向量。
        /// </summary>
        private void SetLegalIV()
        {
            //对称算法的初始化向量
            //对于DES, TripleDES, RC2向量的长度为8字节
            //对于Rijndael算法，向量长度为16字节
            //可以人工输入，也可以随机生成，方法是：des.GenerateIV();
            //在这儿为了简单起见，我们使用固定的值，
            //也可以按照调整key的办法来调整向量。
            switch (mAlgorithm)
            {
                case EncryptEnum.Rijndael:
                    mCryptoService.IV = new byte[] { 0xf, 0x6f, 0x13, 0x2e, 0x35, 0xc2, 0xcd, 0xf9, 0x5, 0x46, 0x9c, 0xea, 0xa8, 0x4b, 0x73, 0xcc };
                    break;
                default:
                    mCryptoService.IV = new byte[] { 0xf, 0x6f, 0x13, 0x2e, 0x35, 0xc2, 0xcd, 0xf9 };
                    break;
            }
        }
        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="plainText">待加密字符串</param>
        /// <returns></returns>
        public string Encrypt(string plainText)
        {
            byte[] cryptoByte = null;
            try
            {
                byte[] plainByte = System.Text.UTF8Encoding.UTF8.GetBytes(plainText);
                byte[] keyByte = GetLegalKey();

                // 设定key和向量
                mCryptoService.Key = keyByte;
                SetLegalIV();
                // 加密对象
                ICryptoTransform cryptoTransform = mCryptoService.CreateEncryptor();
                // 内存流对象
                MemoryStream ms = new MemoryStream();
                // 初始化加密流
                CryptoStream cs = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Write);
                // 将加密后的数据写入加密流
                cs.Write(plainByte, 0, plainByte.Length);
                cs.FlushFinalBlock();
                cs.Close();
                // 得到加密后的数据
                cryptoByte = ms.ToArray();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }
            // 将数据转换成base64字符串
            return Convert.ToBase64String(cryptoByte, 0, cryptoByte.GetLength(0));
        }
        /// <summary>
        /// 解密
        /// 解密的时候用的key和向量必须和加密的时候用的一样
        /// </summary>
        /// <param name="cryptoText">待解密字符串</param>
        /// <returns></returns>
        public string Decrypt(string cryptoText)
        {
            // 从base64字符串转换成字节
            if (cryptoText == null)
                return null;

            byte[] cryptoByte = Convert.FromBase64String(cryptoText);
            byte[] keyByte = GetLegalKey();

            //设定key和向量
            mCryptoService.Key = keyByte;
            SetLegalIV();
            // 解密对象
            ICryptoTransform cryptoTransform = mCryptoService.CreateDecryptor();
            try
            {
                // 内存流对象
                MemoryStream ms = new MemoryStream(cryptoByte, 0, cryptoByte.Length);
                // 初始化一个解密流对象
                CryptoStream cs = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Read);
                // 从解密流对象中得到解密后的数据
                StreamReader sr = new StreamReader(cs);
                return sr.ReadToEnd();
            }
            catch
            {
                return null;
            }
        }
        #region 示例
        //public static void Main(string[] args)
        //{
        //    string plainText = "a example 小朋友";
        //    string key = "wqdj~yriu!@*k0_^fa7431%p$#=@hd+&";
        //    string testEnc = null;
        //    string testDec = null;

        //    Console.WriteLine("Plain text: " + plainText);
        //    Console.WriteLine("Key: " + key + "\n");

        //    EncryptClass enc1 = new EncryptClass();
        //    enc1.Key = key;
        //    testEnc = enc1.Encrypt(plainText);
        //    testDec = enc1.Decrypt(testEnc);
        //    Console.WriteLine("Use Encrypt algorithm: " + enc1.mAlgorithm);
        //    Console.WriteLine("Encrypted text: " + testEnc);
        //    Console.WriteLine("Decrypted text: " + testDec + "\n");

        //    EncryptClass enc2 = new EncryptClass(ServiceProviderEnum.TripleDES);
        //    enc2.Key = key;
        //    testEnc = enc2.Encrypt(plainText);
        //    testDec = enc2.Decrypt(testEnc);
        //    Console.WriteLine("Use Encrypt algorithm: " + enc2.mAlgorithm);
        //    Console.WriteLine("Encrypted text: " + testEnc);
        //    Console.WriteLine("Decrypted text: " + testDec + "\n");

        //    EncryptClass enc3 = new EncryptClass("rc2");
        //    enc3.Key = key;
        //    testEnc = enc3.Encrypt(plainText);
        //    testDec = enc3.Decrypt(testEnc);
        //    Console.WriteLine("Use Encrypt algorithm: " + enc3.mAlgorithm);
        //    Console.WriteLine("Encrypted text: " + testEnc);
        //    Console.WriteLine("Decrypted text: " + testDec + "\n");

        //    Console.Read();
        //}
        #endregion
    }
}


